AWS Client VPN を CDK で作ってみた
この記事はアノテーション株式会社 AWS Technical Support Advent Calendar 2021のカレンダー | Advent Calendar 2021 - Qiita20日目の記事です。
はじめに
こんにちは。アノテーションの小川です。
以前、ちょっとした検証で AWS Client VPN を作成し、用済み後すぐに削除したことがありました。
作成自体は難しくありませんが、AWS Client VPN を使用するためにはクライアントVPNエンドポイントの作成だけで終わりではありません。
作成後、サブネットに関連付けたり、認証ルールの設定などを行う必要があります。
業務で AWS Client VPN を作成する機会はあまりありませんが、必要が出た際にコンソールで作業するのも時間がかかりそうです。
そのため、次回以降の作業を楽にするために、少ない記述でよしなにリソースを作成してくれる AWS CDK を使って、AWS Client VPN(相互認証) を作ってみました。
このブログでは、以下の手順で AWS Client VPN を作成し、疎通を確認します。
- 証明書の作成
- VPC、クライアントVPNエンドポイントの作成(CDK)
- 疎通確認(SSH)
- クライアントVPNエンドポイントの削除(CDK)
なお、環境は次のとおりです。
$ sw_vers ProductName: macOS ProductVersion: 11.6 BuildVersion: 20G165 $ cdk --version 2.1.0 (build f4f18b1)
では、手順通りに進めていきます。
証明書の作成
クライアントVPNエンドポイントの認証は複数ありますが、今回は相互認証を使用します。
相互認証に必要な証明書の作成は、公式ドキュメント記載のコマンドを実行して作成します。
コマンドを打つだけとはいえ手間だったので、証明書の作成から AWS Certificate Manager へのインポートまでのコマンドを羅列したシェルスクリプトを実行しました。
インポートする証明書等を格納するディレクトリは事前に作成し、CERTIFICATE_DIRECTORY にそのパスを代入します。
#! /bin/bash CERTIFICATE_DIRECTORY=$HOME/aws/certificate git clone https://github.com/OpenVPN/easy-rsa.git cd easy-rsa/easyrsa3 ./easyrsa init-pki echo \n | ./easyrsa build-ca nopass ./easyrsa build-server-full server nopass ./easyrsa build-client-full client1.domain.tld nopass cp pki/ca.crt $CERTIFICATE_DIRECTORY cp pki/issued/server.crt $CERTIFICATE_DIRECTORY cp pki/private/server.key $CERTIFICATE_DIRECTORY cp pki/issued/client1.domain.tld.crt $CERTIFICATE_DIRECTORY cp pki/private/client1.domain.tld.key $CERTIFICATE_DIRECTORY cd $CERTIFICATE_DIRECTORY aws acm import-certificate --certificate fileb://server.crt --private-key fileb://server.key --certificate-chain fileb://ca.crt
コマンドが正常終了すると CertificateArn が返却されるので、メモしておきます(CDKで使用)。
{ "CertificateArn": "arn:aws:acm:ap-northeast-1:111111111111:certificate/8add79e2-8ede-4b6f-a1de-xxxxxxxxxxxx" }
VPC、クライアントVPNエンドポイントの作成
まず、CDKプロジェクトを作成します。
CDK使用の前提条件はこちら をご覧ください。
mkdir vpn-resources cd vpn-resources cdk init app --language=typescript
異なるターミナルで以下を実行し(CDKプロジェクトのディレクトリで)、ファイルの変更があれば自動でコンパイルするようにしておきます。
npm run watch
以下の3つのファイルを作成します。
#!/usr/bin/env node import 'source-map-support/register'; import * as cdk from 'aws-cdk-lib'; import { VpcStack } from '../lib/vpc-stack'; import { VpnStack } from '../lib/vpn-stack'; const app = new cdk.App(); const env = { account: '111111111111', region: 'ap-northeast-1' } const vpcStack = new VpcStack(app, 'VpcStack', { stackName: 'vpc-stack', env: env }); new VpnStack(app, 'VpnStack', vpcStack.vpc, vpcStack.vpnAssociateSubnet, { stackName: 'vpn-stack', env: env });
import { aws_ec2 as ec2, Stack, StackProps } from 'aws-cdk-lib'; import { Construct } from 'constructs'; export class VpcStack extends Stack { public readonly vpc: ec2.Vpc; public readonly vpnAssociateSubnet: ec2.Subnet; constructor(scope: Construct, id: string, props?: StackProps) { super(scope, id, props); this.vpc = new ec2.Vpc(this, 'PrivateVPC', { cidr: '10.0.0.0/16', subnetConfiguration: [] }) this.vpnAssociateSubnet = new ec2.Subnet(this, 'PrivateSubnet1a', { cidrBlock: '10.0.0.0/24', vpcId: this.vpc.vpcId, availabilityZone: 'ap-northeast-1a' }) new ec2.Subnet(this, 'PrivateSubnet1c', { cidrBlock: '10.0.10.0/24', vpcId: this.vpc.vpcId, availabilityZone: 'ap-northeast-1c' }) } }
import { aws_ec2 as ec2, Stack, StackProps } from 'aws-cdk-lib'; import { Construct } from 'constructs'; export class VpnStack extends Stack { constructor(scope: Construct, id: string, vpc: ec2.Vpc, associateSubnet: ec2.Subnet, props?: StackProps) { super(scope, id, props); const clientCidr = '10.100.0.0/16' const certificateArn = 'arn:aws:acm:ap-northeast-1:111111111111:certificate/8add79e2-8ede-4b6f-a1de-xxxxxxxxxxxx' const dnsServer = '10.0.0.2' new ec2.ClientVpnEndpoint(this, 'vpn', { vpc: vpc, cidr: clientCidr, serverCertificateArn: certificateArn, clientCertificateArn: certificateArn, dnsServers: [dnsServer], splitTunnel: true, vpcSubnets: { subnets: [ec2.Subnet.fromSubnetId(this, 'vpnPrivateSubnet1a', associateSubnet.subnetId)] } }) } }
以下のコマンドで、CloudFormation Stack を作成します。
$ cdk ls VpcStack VpnStack $ cdk deploy --all
作成完了後、クライアントVPNエンドポイントのコンソール画面に移り、クライアント設定のダウンロードをして設定ファイルを編集します。
- 4行目の remote に記載されているクライアントVPNエンドポイントの DNS 名の先頭にランダムな文字列を追記(参考)
- client1.domain.tld.crt と client1.domain.tld.keyをそれぞれ cert と key に追記
client dev tun proto udp remote {random_string}.cvpn-endpoint-xxxxxxxx.prod.clientvpn.ap-northeast-1.amazonaws.com 443 remote-random-hostname resolv-retry infinite nobind remote-cert-tls server cipher AES-256-GCM verb 3 <ca> -----BEGIN CERTIFICATE----- xxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxx -----END CERTIFICATE----- </ca> <cert> -----BEGIN CERTIFICATE----- Contents of client certificate (.crt) file -----END CERTIFICATE----- </cert> <key> -----BEGIN PRIVATE KEY----- Contents of private key (.key) file -----END PRIVATE KEY----- </key> reneg-sec 0
クライアントVPNエンドポイント接続に使用するクライアントのダウンロード・設定はこちらをご覧ください。
疎通確認(SSH)
クライアントの設定が完了後、接続を開始します。
接続が成功したので、SSHでの疎通確認を行います。
EC2は事前に作成しており、セキュリティグループのインバウンドルールでクライアントVPNエンドポイントのセキュリティグループを許可しています。
$ ssh -i key.pem [email protected] The authenticity of host '10.0.10.251 (10.0.10.251)' can't be established. ECDSA key fingerprint is SHA256:xxxxxxxxxx Are you sure you want to continue connecting (yes/no/[fingerprint])? yes Warning: Permanently added '10.0.10.251' (ECDSA) to the list of known hosts. __| __|_ ) _| ( / Amazon Linux 2 AMI ___|\___|___| https://aws.amazon.com/amazon-linux-2/ [ec2-user@ip-10-0-10-251 ~]$
疎通確認完了です。
クライアントVPNエンドポイントの削除
CDKでAWS Client VPN を作成し、疎通まで確認できたので削除します。
私の場合、VPCは別の用途でも使用するため残しますので、VpnStackのみ削除します。
$ cdk destroy VpnStack
おわりに
当初は通常の CloudFormation テンプレートで作成するつもりでしたが、クライアントVPNエンドポイントだけでなく関連付けるサブネットや接続ログのためのCloudWatch Logs ロググループなども記述する必要があったり、記述する量がどうしても増えてしまいます。
CDKはあまり使用したことがありませんでしたが、少ない記述で関連するリソースを自動で作成してくれるのは非常に便利だと思いました。
参考資料
アノテーション株式会社について
アノテーション株式会社は、クラスメソッド社のグループ企業として「オペレーション・エクセレンス」を担える企業を目指してチャレンジを続けています。「らしく働く、らしく生きる」のスローガンを掲げ、さまざまな背景をもつ多様なメンバーが自由度の高い働き方を通してお客様へサービスを提供し続けてきました。現在当社では一緒に会社を盛り上げていただけるメンバーを募集中です。少しでもご興味あれば、アノテーション株式会社WEBサイトをご覧ください。